package org.apache.maven.plugin.nar;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.bcel.classfile.ClassFormatException;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.toolchain.Toolchain;
import org.apache.maven.toolchain.ToolchainManager;
import org.codehaus.plexus.compiler.util.scan.InclusionScanException;
import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
import org.codehaus.plexus.compiler.util.scan.StaleSourceScanner;
import org.codehaus.plexus.compiler.util.scan.mapping.SingleTargetSourceMapping;
import org.codehaus.plexus.compiler.util.scan.mapping.SuffixMapping;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.StringUtils;
/**
* Sets up the javah configuration
*
* @author Mark Donszelmann
*/
public class Javah
{
/**
* Javah command to run.
*
* @parameter default-value="javah"
*/
private String name = "javah";
/**
* Add boot class paths. By default none.
*
* @parameter
*/
private List/* <File> */bootClassPaths = new ArrayList();
/**
* Add class paths. By default the classDirectory directory is included and all dependent classes.
*
* @parameter
*/
private List/* <File> */classPaths = new ArrayList();
/**
* The target directory into which to generate the output.
*
* @parameter expression="${project.build.directory}/nar/javah-include"
* @required
*/
private File jniDirectory;
/**
* The class directory to scan for class files with native interfaces.
*
* @parameter expression="${project.build.directory}/classes"
* @required
*/
private File classDirectory;
/**
* The set of files/patterns to include Defaults to "**\/*.class"
*
* @parameter
*/
private Set includes = new HashSet();
/**
* A list of exclusion filters.
*
* @parameter
*/
private Set excludes = new HashSet();
/**
* The granularity in milliseconds of the last modification date for testing whether a source needs recompilation
*
* @parameter default-value="0"
* @required
*/
private int staleMillis = 0;
/**
* The directory to store the timestampfile for the processed aid files. Defaults to jniDirectory.
*
* @parameter
*/
private File timestampDirectory;
/**
* The timestampfile for the processed class files. Defaults to name of javah.
*
* @parameter
*/
private File timestampFile;
private AbstractCompileMojo mojo;
public Javah()
{
}
public final void setAbstractCompileMojo( AbstractCompileMojo mojo )
{
this.mojo = mojo;
}
protected final List getClassPaths()
throws MojoExecutionException
{
if ( classPaths.isEmpty() )
{
try
{
classPaths.addAll( mojo.getMavenProject().getCompileClasspathElements() );
// Add jar dependencies from the local repository
ArtifactRepository repository = mojo.getLocalRepository();
for ( Iterator i = mojo.getMavenProject().getDependencyArtifacts().iterator(); i.hasNext(); )
{
Artifact a = (Artifact) i.next();
if ( Artifact.SCOPE_COMPILE.equals( a.getScope() ) && a.getArtifactHandler().isAddedToClasspath() )
classPaths.add( new File( repository.getBasedir(), repository.pathOf(a)).getAbsolutePath() );
}
}
catch ( DependencyResolutionRequiredException e )
{
throw new MojoExecutionException( "JAVAH, cannot get classpath", e );
}
}
return classPaths;
}
protected final File getJniDirectory()
{
if ( jniDirectory == null )
{
jniDirectory = new File( mojo.getMavenProject().getBuild().getDirectory(), "nar/javah-include" );
}
return jniDirectory;
}
protected final File getClassDirectory()
{
if ( classDirectory == null )
{
classDirectory = new File( mojo.getMavenProject().getBuild().getDirectory(), "classes" );
}
return classDirectory;
}
protected final Set getIncludes()
{
if ( includes.isEmpty() )
{
includes.add( "**/*.class" );
}
return includes;
}
protected final File getTimestampDirectory()
{
if ( timestampDirectory == null )
{
timestampDirectory = getJniDirectory();
}
return timestampDirectory;
}
protected final File getTimestampFile()
{
if ( timestampFile == null )
{
timestampFile = new File( name );
}
return timestampFile;
}
public final void execute()
throws MojoExecutionException, MojoFailureException
{
getClassDirectory().mkdirs();
try
{
SourceInclusionScanner scanner = new StaleSourceScanner( staleMillis, getIncludes(), excludes );
if ( getTimestampDirectory().exists() )
{
scanner.addSourceMapping( new SingleTargetSourceMapping( ".class", getTimestampFile().getPath() ) );
}
else
{
scanner.addSourceMapping( new SuffixMapping( ".class", ".dummy" ) );
}
Set classes = scanner.getIncludedSources( getClassDirectory(), getTimestampDirectory() );
if ( !classes.isEmpty() )
{
Set files = new HashSet();
for ( Iterator i = classes.iterator(); i.hasNext(); )
{
String file = ( (File) i.next() ).getPath();
JavaClass clazz = NarUtil.getBcelClass( file );
Method[] method = clazz.getMethods();
for ( int j = 0; j < method.length; j++ )
{
if ( method[j].isNative() )
{
files.add( clazz.getClassName() );
}
}
}
if ( !files.isEmpty() )
{
getJniDirectory().mkdirs();
getTimestampDirectory().mkdirs();
String javah = getJavah();
mojo.getLog().info( "Running " + javah + " compiler on " + files.size() + " classes..." );
int result = NarUtil.runCommand( javah, generateArgs( files ), null, null, mojo.getLog() );
if ( result != 0 )
{
throw new MojoFailureException( javah + " failed with exit code " + result + " 0x"
+ Integer.toHexString( result ) );
}
FileUtils.fileWrite( getTimestampDirectory() + "/" + getTimestampFile(), "" );
}
}
}
catch ( InclusionScanException e )
{
throw new MojoExecutionException( "JAVAH: Class scanning failed", e );
}
catch ( IOException e )
{
throw new MojoExecutionException( "JAVAH: IO Exception", e );
}
catch ( ClassFormatException e )
{
throw new MojoExecutionException( "JAVAH: Class could not be inspected", e );
}
}
private String[] generateArgs( Set/* <String> */classes )
throws MojoExecutionException
{
List args = new ArrayList();
if ( !bootClassPaths.isEmpty() )
{
args.add( "-bootclasspath" );
args.add( StringUtils.join( bootClassPaths.iterator(), File.pathSeparator ) );
}
args.add( "-classpath" );
args.add( StringUtils.join( getClassPaths().iterator(), File.pathSeparator ) );
args.add( "-d" );
args.add( getJniDirectory().getPath() );
if ( mojo.getLog().isDebugEnabled() )
{
args.add( "-verbose" );
}
if ( classes != null )
{
for ( Iterator i = classes.iterator(); i.hasNext(); )
{
args.add( i.next() );
}
}
return (String[]) args.toArray( new String[args.size()] );
}
private String getJavah() throws MojoExecutionException, MojoFailureException {
String javah = null;
// try toolchain
Toolchain toolchain = getToolchain();
if (toolchain != null) {
javah = toolchain.findTool( "javah" );
}
// try java home
if (javah == null) {
File javahFile = new File( mojo.getJavaHome( mojo.getAOL() ), "bin" );
javah = new File( javahFile, name ).getAbsolutePath();
}
// forget it...
if (javah == null) {
throw new MojoExecutionException( "NAR: Cannot find 'javah' in Toolchain or on JavaHome" );
}
return javah;
}
//TODO remove the part with ToolchainManager lookup once we depend on
//2.0.9 (have it as prerequisite). Define as regular component field then.
private Toolchain getToolchain()
{
Toolchain toolChain = null;
ToolchainManager toolchainManager = ((NarJavahMojo)mojo).getToolchainManager();
if ( toolchainManager != null )
{
toolChain = toolchainManager.getToolchainFromBuildContext( "jdk", ((NarJavahMojo)mojo).getSession() );
}
return toolChain;
}
}